home *** CD-ROM | disk | FTP | other *** search
- ;
- ; CLOCK.MAC -- Version 1.1 -- for Commodore C-128 CP/M Plus
- ;
- ; A utility to read "The Right Time" clock-calendar and set the
- ; system time and date under CP/M Plus on the Commodore 128.
- ;
- ; USAGE:
- ;
- ; CLOCK
- ;
- ; No parameters are needed.
- ;
- ; There is one small bug. When you call CLOCK from the CP/M command line,
- ; if the system time was previously set to a time later than the real time
- ; that the clock provides, the date will be advanced a day by the warm
- ; boot. The apparent reason for this is that when the BDOS or CCP checks
- ; the time and finds it is earlier than the last time it checked, it
- ; assumes it's a new day and that the date should be incremented. I don't
- ; know a way around this, but there may be a flag somewhere that I haven't
- ; found yet. This situation should be a rare occurrence for most people.
- ; In any case running CLOCK a second time will set the date correctly.
- ;
- ; Much of this code is not original. For the clock reading routines I
- ; relied heavily on the C-128 native mode driver written and copyrighted
- ; by Stephen Ardelt of Ardelt Engineering, manufacturers of The Right Time
- ; clock-calendar. The most important changes were translating 6502 code
- ; to Z80 code and using Z80 in and out instructions to access the I/O chips,
- ; which are not visible in banks 1 or 0, the banks in which CP/M operates.
- ;
- ; Most of the subroutine to put the date in DRI format was taken from
- ; UNDATE.ASM, a procedure written by S. Kluger of the El Paso RCP/M. It
- ; is in the public domain. The original routine was written to read the
- ; time and date as ASCII strings, so it was modified to accept binary
- ; input.
- ;
- ; The Right Time is a trademark of Ardelt Engineering Company for their
- ; battery-backed clock-calendar for the Commodore 64 and 128. The clock
- ; plugs into the User Port and, if you use a 1670 modem, it will plug into
- ; the back of the clock. The RTC does not interfere with any CP/M functions,
- ; including Drive M: (the RAM disk). You can get more information from
- ; Ardelt Engineering Company
- ; 8175 East 39th Avenue
- ; Denver, CO 80207
- ; (800) 237-2943 for orders
- ; (303) 355-1763 for technical information
- ;
- ; HISTORY:
- ;
- ; Version 1.0 -- December 5, 1987
- ; Gene Pizzetta
- ; 481 Revere Street
- ; Revere, MA 02151
- ; Voice: (617) 284-0891
- ; CompuServe: 72060,505
- ; GEnie: E.Pizzetta
- ; Q-Link: GeneP
- ;
- ; Version 1.1 -- December 7, 1987 -- Added date and time display to console.
- ; Gene Pizzetta
- ;
- ; This program was developed using SLRMAC from SLR Systems, just possibly
- ; the world's best assembler. This source file can be assembled by MAC and
- ; HEXCOM (much more slowly, of course) by changing the filetype from .MAC
- ; to .ASM. MAC will also require that you have Z80.LIB on the default drive.
- ;
- ; Note also that MAC will generate a meaningless error when it finds a line
- ; with an exclamation point in a comment. Ignore the error; the generated
- ; code will be fine.
- ;
- ;
- ; System addresses
- ;
- WBoot equ 0000h ; warm boot
- Bdos equ 0005h ; BDOS entry
- PrtStr equ 09h ; BDOS print string
- SetSCB equ 031h ; BDOS set/get system control block
- BEL equ 07h
- LF equ 0Ah
- CR equ 0Dh
- ;
- ; CIA #1
- ;
- Cia1PA equ 0DC00h ; port A
- Cia1PB equ 0DC01h ; port B
- Cia1DA equ 0DC02h ; data direction register A
- Cia1DB equ 0DC03h ; data direction register B
- Cia1Ten equ 0DC08h ; tenths of a second
- Cia1Sec equ 0DC09h ; seconds
- Cia1Min equ 0DC0Ah ; minutes
- Cia1Hrs equ 0DC0Bh ; hours
- Cia1IC equ 0DC0Dh ; interrupt control register
- Cia1CA equ 0DC0Eh ; control register A
- Cia1CB equ 0DC0Fh ; control register B
- ;
- ; CIA #2
- ;
- Cia2PA equ 0DD00h ; port A
- Cia2PB equ 0DD01h ; port B
- Cia2DA equ 0DD02h ; data direction register A
- Cia2DB equ 0DD03h ; data direction register B
- Cia2Ten equ 0DD08h ; tenths of a second
- Cia2Sec equ 0DD09h ; seconds
- Cia2Min equ 0DD0Ah ; minutes
- Cia2Hrs equ 0DD0Bh ; hours
- Cia2IC equ 0DD0Dh ; interrupt control register
- Cia2CA equ 0DD0Eh ; control register A
- Cia2CB equ 0DD0Fh ; control register B
- ;
- MACLIB Z80
- ;
- org 100h
- ;
- jmp START
- ;
- ; data storage and tables
- ;
- CiaSav: db 0,0,0,0 ; original CIA #2 port configuration
- LoNib: db 0 ; low nibble data during RTC read
- Secs: db 0 ; seconds read from RTC
- Mins: db 0 ; minutes read from RTC
- Hours: db 0 ; hours read from RTC
- Month: db 0 ; month read from RTC
- BMonth db 0 ; binary month
- Date: db 0 ; date read from RTC
- BDate: db 0 ; binary date
- Year: db 0 ; year read from RTC
- BYear db 0 ; binary year
- Jan: db 31 ; January
- Feb: db 28,31,30,31,30 ; February to June
- db 31,31,30,31,30 ; July to November
- Days: dw 0 ; total days sum for UNDATE
- ScbPB: db 58h,0FEh ; SCB parameter block for
- ScbVal: dw 0 ; ..BDOS Function 49
- ;
- ; messages
- ;
- MsgSOn: db 'C-128 CLOCK Version 1.1',CR,LF,LF,'$'
- MsgNCk: db BEL,' Clock not installed.',CR,LF,'$'
- MsgBat: db BEL,' Clock not set or battery dead.',CR,LF,'$'
- MsgDRI: db BEL,' CP/M date out of range.',CR,LF,'$'
- MsgDat: db ' Date: '
- AMonth: db 0,0,'/'
- ADay: db 0,0,'/'
- AYear: db 0,0,CR,LF,'$'
- MsgTim: db ' Time: '
- AHours: db 0,0,':'
- AMins: db 0,0,':'
- ASecs: db 0,0,CR,LF,'$'
- ;
- START: lxi d,MsgSOn ; print sign-on
- mvi c,PrtStr
- call Bdos
- ;
- di ; disable interrupts
- mvi a,07Fh ; disable NMI's
- lxi b,Cia2IC
- outp a
- lxi b,Cia2CA ; set bit 7 of CIA#2 control register A
- inp a ; to 60hz
- ani 07Fh
- outp a
- lxi b,Cia2CB ; set bit 7 of CIA#2 control register B
- inp a ; to clock
- ani 07Fh
- outp a
- lxi b,Cia1CA ; set bit 7 of CIA#1 control register A
- inp a ; to 60hz
- ani 07Fh
- outp a
- lxi b,Cia1CB ; set bit 7 of CIA#1 control register B
- inp a ; to clock
- ani 07Fh
- outp a
- lxi b,Cia2PA ; save CIA#2 port setup (4 bytes)
- lxi d,CiaSav
- inp a
- stax d ; (1)
- lxi b,Cia2PB
- inx d
- inp a
- stax d ; (2)
- lxi b,Cia2DA
- inx d
- inp a
- stax d ; (3)
- lxi b,Cia2DB
- inx d
- inp a
- stax d ; (4)
- ;
- ; Now we're ready to open the channels to The Right Time clock
- ;
- mvi a,03Fh ; set CIA#2 DDR A to default
- lxi b,Cia2DA
- outp a
- mvi a,0FFh ; set CIA#2 DDR B to outputs
- lxi b,Cia2DB
- outp a
- lxi b,Cia2PA ; make sure PA2 is high (bit 3 = 4)
- inp a
- ori 04h
- outp a
- lxi b,Cia2PB ; set PB5 high, all others low
- mvi a,20h
- outp a
- lxi b,Cia2PA ; set PA2 low (chip select)
- inp a
- ani 0FBh
- outp a
- lxi b,Cia2PB ; set PB5 low first time (counter goes to 1)
- mvi a,00h
- outp a
- mvi d,08h ; load reg D for loop
- LOOP: mvi a,20h ; bring PB5 high (no count)
- outp a
- mvi a,00h ; bring PB5 low (advance count)
- outp a ; 8 times only (2 thru 9)
- dcr d
- jrz STOP
- jr LOOP
- STOP: lxi b,Cia1Hrs ; stop CIA clocks
- outp a
- lxi b,Cia2Hrs
- outp a
- ;
- ; The RTC channels are open and the CIA time-of-day clocks are stopped.
- ; Now we read the clock and set the CIA time. First, the seconds ...
- ;
- CLKSET: mvi d,00h ; clear D for start of read
- call RDRTC ; read seconds (result in A, D=0 on entry,
- ; ..D=2 on return
- cpi 60h ; less than 60 seconds BCD?
- jnc NOCLK ; (no, no clock)
- lxi b,Cia1Sec ; set CIA seconds
- outp a
- lxi b,Cia2Sec
- outp a
- sta Secs
- ;
- ; We've set the seconds. Now for the minutes ...
- ;
- call RDRTC ; read minutes (D=2 on entry, D=4 on return)
- lxi b,Cia1Min ; set CIA minutes
- outp a
- lxi b,Cia2Min
- outp a
- sta Mins
- ;
- ; Now for the hours. RTC hours are in 24-hour format, but the CIA clocks
- ; use a 12-hour format with an am-pm flag. We'll have to make the conversion
- ; remembering that we're dealing with time in Binary Coded Decimal ...
- ;
- call RDRTC ; read hours and covert to 12 hour format
- mov e,a ; store data in E
- ani 3Fh ; strip bits 6 and 7 (time is 0-23 hours)
- sta Hours
- cpi 00h ; is it 0 o'clock?
- jrz Add92 ; (yes, 12 am)
- cpi 12h ; less than 12 (am)?
- jrc NoFlag ; (yes, no pm flag needed)
- cpi 20h ; less than 20 BCD?
- jrc Sub18 ; (yes)
- cpi 22h ; less than 22 BCD?
- jrc Sub24 ; (yes)
- jr Sub18 ; here we have 22 or 23 hours
- Add92: mvi a,92h ; save 92 (flag goes down on CIA writes)
- NoFlag: jr Store
- Sub18: sui 12h ; subtract 18 decimal
- ori 80h ; set pm flag
- jr Store
- Sub24: sui 18h ; subtract 24 decimal
- ori 80h ; set pm flag
- Store: lxi b,Cia1Hrs ; set CIA hours and am-pm flag
- outp a
- lxi b,Cia2Hrs
- outp a
- ;
- ; The clocks are set. Now we get the date information and store it.
- ; We'll deal with it after we've finished with the clock and re-enabled
- ; interrupts. (We're trying to move fast here.)
- ;
- call WRADDR ; read day of week from RTC
- call RDDATA ; ..and discard it (CP/M doesn't use it)
- inr d ; D=7
- ;
- call RDRTC ; read date (D=7 on entry; D=9 on return)
- sta Date ; ..and store it
- cpi 00h ; is date 0?
- jz NOBAT ; (yes, there's a problem)
- ;
- call RDRTC ; read month (D=9 on entry; D=11 on return)
- sta Month ; ..and store it
- ;
- call RDRTC ; read year (D=11 on entry; D=13 on return)
- sta Year ; ..and store it
- ;
- mvi d,00h ; clear D so we can check for RTC ripple
- call RDRTC
- mov e,a ; move RTC seconds to E
- lxi b,Cia2Sec ; read CIA #2 seconds
- cmp e ; are they the same
- jrz DATSET ; (yes)
- jmp CLKSET ; if they're different read and set again
- ;
- ; We've set the CIA clocks, which the BIOS uses to update the CP/M time
- ; in the System Control Block. We've also read the date from The Right
- ; Time clock and stored it. Now we have to convert it to DRI CP/M format
- ; and let CP/M know what it is.
- ;
- DATSET: call CLSCLK ; close down the clock channels
- ei ; enable interrupts
- ;
- lda Month ; convert month to binary
- call BCDBIN
- sta BMonth
- lda Date ; convert day to binary
- call BCDBIN
- sta BDate
- lda Year ; convert year in to binary
- call BCDBIN
- sta BYear
- cpi 04Eh ; is year less that 1978?
- jc NOTDRI ; (yes, invalid date)
- call UNDATE ; put the date in DRI format
- ;
- ; Now everything's ready to set the date in the System Control Block.
- ; BDOS Function 104 is for setting the date and time, but we won't be
- ; using it. You can't set the date with Function 104 without setting
- ; the time, but we've already set the time more accurately (Function 104
- ; won't accept seconds in the time specification). Instead, we'll use
- ; BDOS Function 49, Get/Set System Control Block, and write the date
- ; directly into the SCB.
- ;
- shld ScbVal ; the date in DRI format is in HL
- lxi d,ScbPB ; ..put it in the SCB parameter block
- mvi c,SetSCB ; ..and call the BDOS
- call Bdos
- ;
- ; We're done now, but before we leave we'll report the date and time we
- ; just set. We've saved the BCD from the RTC so we'll convert it to
- ; hexadecimal ASCII and print in at the console.
- ;
- lda Month ; get month
- call BINHEX ; ..convert it
- shld AMonth ; ..and store it in string
- lda Date ; get date
- call BINHEX
- shld ADay
- lda Year ; get year
- call BINHEX
- shld AYear
- ;
- lxi d,MsgDat ; print date
- mvi c,PrtStr
- call Bdos
- ;
- lda Hours ; get hours
- call BINHEX
- shld AHours
- lda Mins ; get minutes
- call BINHEX
- shld AMins
- lda Secs ; get seconds
- call BINHEX
- shld ASecs
- ;
- lxi d,MsgTim ; print time
- mvi c,PrtStr
- call Bdos
- ;
- jmp WBoot ; salute! and happy days!
- ;
- ; We come here if the year was earlier than 1978
- ;
- NOTDRI: lxi d,MsgDRI ; tell 'em it's wrong
- mvi c,PrtStr
- call Bdos
- jmp WBoot ; ..and exit
- ;
- ; We come here if the clock is not installed in the User Port
- ;
- NOCLK: call CLSCLK ; close the clock so we don't crash
- ei ; enable interrupts
- lxi d,MsgNCk
- mvi c,PrtStr ; say the obvious
- call Bdos
- jmp WBoot ; ..and exit
- ;
- ; We come here if the clock is not set, or if the battery is dead
- ;
- NOBAT: call CLSCLK ; system crashes if we leave it open
- ei ; enable interrupts
- lxi d,MsgBat
- mvi c,PrtStr ; give 'em the bad news
- call Bdos
- jmp WBoot ; ..and exit
- ;
- ; Subroutine: RDRTC -- read The Right Time clock
- ;
- RDRTC: lxi h,LoNib
- call WRADDR
- call RDDATA
- mov m,a ; store low nibble
- inr d ; increment D
- call WRADDR
- call RDDATA ; reading high nibble
- ral ; shift 4 bits left
- ral
- ral
- ral
- ora m ; add tens and ones
- inr d
- ret
- ;
- ; Subroutine: WRADDR -- write read address to The Right Time clock
- ;
- WRADDR: mvi a,0FFh
- lxi b,Cia2DB ; set CIA #2 port B to output
- outp a
- mov a,d ; move D to A
- adi 10h ; add address write to data address
- lxi b,Cia2PB ; send to port B
- outp a
- nop ; delay .5 microsecond minimum
- nop
- mov a,d ; move data address to A
- outp a ; ..and send to port B
- mvi a,00h ; clear data lines
- outp a
- ret
- ;
- ; Subroutine: RDDATA -- read data from The Right Time clock
- ;
- RDDATA: lxi b,Cia2DB ; set CIA #2 DDR for port B
- inp a ; msn=output; lsn=input
- ani 0F0h
- outp a
- mvi a,00h ; clear lines
- lxi b,Cia2PB
- outp a
- mvi a,40h ; open read line
- outp a
- nop ; delay 6 microseconds minimum
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- inp a ; get data + 64
- mov e,a ; ..and move it to E
- mvi a,00h ; close read line
- outp a
- nop ; delay 1 microsecond minimum
- nop
- mov a,e ; get data back in A
- ani 0Fh ; mask off non-data
- ret
- ;
- ; Subroutine: CLSCLK -- close The Right Time clock
- ;
- CLSCLK: mvi a,00h ; clear for write to 1/10 second register
- lxi b,Cia1Ten
- outp a ; this restarts CIA clocks
- lxi b,Cia2Ten
- outp a
- lxi b,Cia2PA ; set PA2 high (close RTC enable)
- inp a
- ori 04h ; only that bit is changed
- outp a
- ;
- lxi b,Cia2PA ; restore CIA#2 port setup (4 bytes)
- lxi d,CiaSav
- ldax d
- outp a ; (1)
- lxi b,Cia2PB
- inx d
- ldax d
- outp a ; (2)
- lxi b,Cia2DA
- inx d
- ldax d
- outp a ; (3)
- lxi b,Cia2DB
- inx d
- ldax d
- outp a ; (4)
- ret
- ;
- ; Subroutine: BCDBIN -- converts BDC number in A to binary number in A
- ;
- BCDBIN: mov b,a ; save original value in B
- ani 0F0h ; mask high nibble
- rrc ; shift right
- mov c,a ; C = high nibble * 8
- rrc ; shift right twice more
- rrc ; A = high nibble * 2
- add c
- mov c,a ; C = high nibble * (8 + 2)
- mov a,b ; get original value back
- ani 0Fh ; make high nibble
- add c ; add to binary high nibble
- ret
- ;
- ; Subroutine: BINHEX -- converts binary number to hexadecimal ASCII (or
- ; Binary Coded Decimal to ASCII decimal). Binary byte in A, two hexadecimal
- ; characters in HL (MSB in L, so we can SHLD it into a string).
- ;
- BINHEX: mov b,a ; save original binary
- ani 0F0h ; get high nibble
- rrc ; move high nibble to low nibble
- rrc
- rrc
- rrc
- call NASCII ; convert high nibble to ASCII
- mov l,a ; return high nibble to H
- mov a,b
- ani 0Fh ; get low nibble
- call NASCII ; convert low nibble to ASCII
- mov h,a ; return low nibble in L
- ret
- ;
- ; Subroutine: NASCII -- (used by BINHEX) converts a hexadecimal digit to
- ; ASCII. Binary data in A (lower nibble), returns ASCII character in A.
- ; Uses only A and F.
- ;
- NASCII: cpi 10
- jrc Nas1 ; jump if high nibble < 10
- adi 7 ; or add 7 so after adding '0' the
- ; ..character with be 'A'..'F'
- Nas1: adi '0' ; add ASCII 0 to make a character
- ret
- ;
- ; Subroutine: UNDATE -- converts a binary date to DRI format, a 16-bit
- ; binary integer for the number of days since January 1, 1978.
- ;
- UNDATE: lda BYear ; get the binary date
- mvi b,78 ; set up a years counter
- mov c,a ; year into C
- ani 0FCh ; is it a leap year?
- cmp c
- mvi a,28 ; assume it's not
- jrnz ItsNot
- inr a
- ItsNot: sta Feb
- lxi h,0 ; set a day counter
- YLoop: mov a,c ; get the year
- cmp b ; is it 78 yet?
- jrz YDone ; (yes, we're through here)
- lxi d,365 ; set number of days in a year
- dcr c ; decrement the year
- dcr a ; ..both times
- ani 0FCh ; ..and check for a leap year
- cmp c
- jrnz NoLeap ; (no leap)
- inx d ; make days 366
- NoLeap: dad d ; now sum the days up
- jr YLoop
- ;
- ; Years are done, now for the months
- ;
- YDone: shld Days ; save days so far
- lda BMonth ; get the binary month
- lxi b,Jan ; point to the months table
- mvi h,0
- mov l,a
- dad b ; set the months table pointer
- mov b,h
- mov c,l
- dcx b
- lhld Days
- mvi d,0 ; get ready to add
- Mloop: dcr a ; decrement the year
- dcx b
- jrz MDone ; (we're done with months)
- push psw
- ldax b ; get days
- mov e,a
- dad d ; ..and add to total
- pop psw
- jr Mloop
- ;
- ; Months are done, now finally the days
- ;
- Mdone: lda BDate ; get the days in binary
- mov e,a ; get ready to add (this is easy)
- mvi d,0
- dad d ; ..add them to total
- ret ; ..and we're done (total in HL)
- ;
- end
- ;